/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Dec. 2009
 *
 */

#include <algorithm>
#include <numeric>
#include <string>

#include "arch/utility.hh"
#include "base/cp_annotate.hh"
#include "base/loader/symtab.hh"
#include "base/timebuf.hh"
#include "base/callback.hh"
#include "config/full_system.hh"
#include "config/the_isa.hh"
#include "config/use_checker.hh"
#include "cpu/exetrace.hh"
#include "cpu/edge/commit.hh"
#include "cpu/edge/thread_state.hh"
#include "params/DerivEdgeCPU.hh"
#include "sim/sim_events.hh"

static const char * classInstBlock[6] = {
    "Seq_100",
    "Bra_100",
    "Cal_100",
    "Ret_100",
    "Seq_Oth",
    "Res_Cat",
};

template <class Impl>
class CommitExitCallBack : public Callback
{
  private:
    SimpleEdgeCommit<Impl> * commitStage;
  public:
    virtual void process();
    CommitExitCallBack(SimpleEdgeCommit<Impl> * commit_stage)
        : commitStage(commit_stage)
    {}
};

template <class Impl>
void
CommitExitCallBack<Impl>::process()
{
    commitStage->dumpInstBlockStat();
}

template <class Impl>
SimpleEdgeCommit<Impl>::TrapEvent::TrapEvent(SimpleEdgeCommit<Impl> *_commit,
                                          ThreadID _tid)
    : Event(CPU_Tick_Pri), commit(_commit), tid(_tid)
{
    this->setFlags(AutoDelete);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::TrapEvent::process()
{
    // This will get reset by commit if it was switched out at the
    // time of this event processing.
    commit->trapSquash[tid] = true;
}

template <class Impl>
const char *
SimpleEdgeCommit<Impl>::TrapEvent::description() const
{
    return "Trap";
}

template <class Impl>
SimpleEdgeCommit<Impl>::SimpleEdgeCommit(CPU *_cpu, DerivEdgeCPUParams *params)
    :
      simPointSim(params->simPointSim),
      cpu(_cpu),
      squashCounter(0),
      executeToCommitDelay(params->executeToCommitDelay),
      commitToExecuteDelay(params->commitToExecuteDelay),
      mapToROBDelay(params->mapToROBDelay),
      fetchToCommitDelay(params->commitToFetchDelay),
      mapWidth(params->mapWidth),
      commitWidth(params->commitWidth),
      numThreads(params->numThreads),
      drainPending(false),
      switchedOut(false),
      trapLatency(params->trapLatency)
{
    if (simPointSim) {
        // We should guarantee that we get a valid simpoint.
        simPoint = new EdgeSimPoint<Impl>(params->simPointStartFunctionName,
                                          params->simPointEndFunctionName,
                                          params->simPointCallNum,
                                          params->simPointRetNum,
                                          params->simPointMode),

        assert(simPoint->isValid());
    } else {
        simPoint = NULL;
    }

    _status = Active;
    _nextStatus = Inactive;
    std::string policy = params->smtCommitPolicy;

    //Convert string to lowercase
    std::transform(policy.begin(), policy.end(), policy.begin(),
                   (int(*)(int)) tolower);

    //Assign commit policy
    if (policy == "aggressive"){
        commitPolicy = Aggressive;

        DPRINTF(EdgeCommit,"Commit Policy set to Aggressive.");
    } else if (policy == "roundrobin"){
        commitPolicy = RoundRobin;

        //Set-Up Priority List
        for (ThreadID tid = 0; tid < numThreads; tid++) {
            priority_list.push_back(tid);
        }

        DPRINTF(EdgeCommit,"Commit Policy set to Round Robin.");
    } else if (policy == "oldestready"){
        commitPolicy = OldestReady;

        DPRINTF(EdgeCommit,"Commit Policy set to Oldest Ready.");
    } else {
        assert(0 && "Invalid SMT Commit Policy. Options Are: {Aggressive,"
               "RoundRobin,OldestReady}");
    }

    for (ThreadID tid = 0; tid < numThreads; tid++) {
        commitStatus[tid] = Idle;
        changedROBNumEntries[tid] = false;
        checkEmptyROB[tid] = false;
        trapInFlight[tid] = false;
        committedStores[tid] = false;
        trapSquash[tid] = false;
        tcSquash[tid] = false;
        microPC[tid] = 0;
        nextMicroPC[tid] = 0;
        PC[tid] = 0;
        nextPC[tid] = 0;
        nextNPC[tid] = 0;
    }
}

template<class Impl>
SimpleEdgeCommit<Impl>::~SimpleEdgeCommit()
{
    if (simPointSim) {
        assert(simPoint);
        delete simPoint;
    }
}

template <class Impl>
std::string
SimpleEdgeCommit<Impl>::name() const
{
    return cpu->name() + ".commit";
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::regStats()
{
    using namespace Stats;

    commitCommittedNopInsts
        .name(name() + ".commitCommittedNopInsts")
        .desc("The number of committed NOP instructions")
        .prereq(commitCommittedNopInsts);

    commitCommittedInsts
        .name(name() + ".commitCommittedInsts")
        .desc("The number of committed instructions including"
              " that haven't been executed but without writes/reads")
        .prereq(commitCommittedInsts);

    commitCommittedWrites
        .name(name() + ".commitCommittedWrites")
        .desc("The number of committed writes")
        .prereq(commitCommittedInsts);

    commitCommittedReads
        .name(name() + ".commitCommittedReads")
        .desc("The number of committed reads")
        .prereq(commitCommittedInsts);

//    commitCommittedUsefulInsts
//    .name(name() + ".commitCommittedUsefulInsts")
//    .desc("The number of committed instructions without NOPs")
//    .prereq(commitCommittedUsefulInsts);

    commitPercentNop
        .name(name() + ".commitPercentNop")
        .desc("The percent of NOP instructions")
        .precision(6);

//    commitCommittedUsefulInsts =
//        commitCommittedInsts - commitCommittedNopInsts;
    commitPercentNop =
        commitCommittedNopInsts / commitCommittedInsts;

    commitCommittedInstBlocks
        .name(name() + ".commitCommittedInstBlocks")
        .desc("The number of committed inst blocks")
        .prereq(commitCommittedInstBlocks);

    commitCommittedSeqInstBlocks
        .name(name() + ".commitCommittedSeqInstBlocks")
        .desc("The number of committed inst blocks with sequential target")
        .prereq(commitCommittedSeqInstBlocks);

    commitSeqInstBlocksFraction
        .name(name() + ".commitSeqInstBlocksFraction")
        .desc("The fraction of the blocks with sequential target")
        .precision(6);

    commitSeqInstBlocksFraction =
        commitCommittedSeqInstBlocks / commitCommittedInstBlocks;

    commitSquashedInsts
        .name(name() + ".commitSquashedInsts")
        .desc("The number of squashed insts skipped by commit")
        .prereq(commitSquashedInsts);

    commitSquashEvents
        .name(name() + ".commitSquashEvents")
        .desc("The number of times commit is told to squash")
        .prereq(commitSquashEvents);

    commitNonSpecStalls
        .name(name() + ".commitNonSpecStalls")
        .desc("The number of times commit has been forced to stall to "
              "communicate backwards")
        .prereq(commitNonSpecStalls);

    branchMispredicts
        .name(name() + ".branchMispredicts")
        .desc("The number of times a branch was mispredicted");

    exitMispredicts
        .name(name() + ".exitMispredicts")
        .desc("The number of times the exit id of a block was mispredicted");

    branchTypeOnlyMispredicts
        .name(name() + ".branchTypeOnlyMispredicts")
        .desc("The total number of branch type mispredicts, without considering the final result.");

    branchTypeOnlyMispredictsBreakDown
        .name(name() + ".MISPREDICTTYPE:branchTypeOnlyMispredictsBreakDown")
        .init(TheISA::NumBranchType)
        .flags(pdf | total | dist)
        .desc("Breakdown of mispredicts by branch type.");

    dynamicBlockByBranchType
        .name(name() + ".MISPREDICTTYPE:dynamicBlockByBranchType")
        .init(TheISA::NumBranchType)
        .flags(pdf | total | dist)
        .desc("Number of blocks by their dynamic branch type.");

    branchTypeOnlyMispredictRateSeq 
        .name(name() + ".MISPREDICTTYPE:branchTypeOnlyMispredictRateSeq")
        .precision(6)
        .desc("Branch type mispredicts for branch type seq.");

    branchTypeOnlyMispredictRateSeq =
        branchTypeOnlyMispredictsBreakDown[0] / dynamicBlockByBranchType[0];

    branchTypeOnlyMispredictRateBranch 
        .name(name() + ".MISPREDICTTYPE:branchTypeOnlyMispredictRateBranch")
        .precision(6)
        .desc("Branch type mispredicts for branch type branch.");

    branchTypeOnlyMispredictRateBranch =
        branchTypeOnlyMispredictsBreakDown[1] / dynamicBlockByBranchType[1];

    branchTypeOnlyMispredictRateCall
        .name(name() + ".MISPREDICTTYPE:branchTypeOnlyMispredictRateCall")
        .precision(6)
        .desc("Branch type mispredicts for branch type call.");

    branchTypeOnlyMispredictRateCall =
        branchTypeOnlyMispredictsBreakDown[2] / dynamicBlockByBranchType[2];

    branchTypeOnlyMispredictRateReturn
        .name(name() + ".MISPREDICTTYPE:branchTypeOnlyMispredictRateReturn")
        .precision(6)
        .desc("Branch type mispredicts for branch type return.");

    branchTypeOnlyMispredictRateReturn =
        branchTypeOnlyMispredictsBreakDown[3] / dynamicBlockByBranchType[3];

    exitAndTypeOnlyMispredicts
        .name(name() + ".exitAndTypeOnlyMispredicts")
        .desc("The total number of exit mispredicts combined with branch type mispredicts, without considering the final result.");

    exitAndBranchMispredicts
        .name(name() + ".exitAndBranchMispredicts")
        .desc("The number of times the exit id and branch target of a block was mispredicted");

    branchTypeMispredicts
        .init(4/* For types of branches*/)
        .name(name() + ".branchTypeMispredicts")
        .desc("The number of mispredicted branches for each type. 0:type, 1:btb, 2:ctb, 3:ras");

    branchTargetBuffersMispredicts
        .init(4/* For types of branches*/)
        .name(name() + ".branchTargetBuffersMispredicts")
        .flags(pdf | total | dist)
        .desc("The number of mispredicted branches for each branch target buffers. 0:seq, 1:btb, 2:ctb, 3:ras");

    exitAndTypeMispredicts
        .name(name() + ".exitAndTypeMispredicts")
        .desc("The number of exit mispredicts combined with type mispredicts");

    seqBranchTypeMispredicts
        .init(2/* Real target is seq or predicted target is seq*/)
        .name(name() + ".seqBranchTypeMispredicts")
        .desc("The number of mispredicted branch types only for sequential targets. 0:the real target is sequential target but the predicted is not, 1: vice versa");

    correctPredictions
     .name(name() + ".correctPredictions")
     .desc("The number of correct prediction")
     .prereq(correctPredictions);

    branchPredRate
        .name(name() + ".branchPredRate")
        .desc("Correct prediction rate of all committed inst blocks. ")
        .precision(6);
    branchPredRate = correctPredictions / commitCommittedInstBlocks;

    branchTypeTendencyInBlock
        .name(name() + ".branchTypeTendencyBlock")
        .desc("Branch type tendency for each static inst block instance")
        .precision(6);

    branchTypeTop100SeqPercent
        .name(name() + ".branchTypeTop100SeqPercent")
        .desc("Percentage of sequential for the top 100 most called instruction blocks")
        .init(100)
        .precision(6);

    branchTypeTop100BranchPercent
        .name(name() + ".branchTypeTop100BranchPercent")
        .desc("Percentage of branch for the top 100 most called instruction blocks")
        .init(100)
        .precision(6);

    branchTypeTop100CallPercent
        .name(name() + ".branchTypeTop100CallPercent")
        .desc("Percentage of call for the top 100 most called instruction blocks")
        .init(100)
        .precision(6);

    branchTypeTop100RetPercent
        .name(name() + ".branchTypeTop100RetPercent")
        .desc("Percentage of ret for the top 100 most called instruction blocks")
        .init(100)
        .precision(6);

    branchTypeTopNClasses
        .name(name() + ".branchTypeTopNClasses")
        .desc("Classes of inst blocks with top 100 instances. First dim categorises static blocks by their number of exit types, second dim categories static blocks by their likelihood of exit types produced dynamically")
        .flags(total | pdf | dist)
        .init(TheISA::NumBranchType - 1, 6);

    branchTypeTopNClasses.ysubnames(classInstBlock);

    branchMispredictsClassified
        .name(name() + ".MISPREDICT:classified_mispredicts")
        .init(6)
        .flags(total | pdf | dist)
        .desc("Number of branfch type mispredicts for different class of hyperblocks.");

    numInstancesClassified
        .name(name() + ".MISPREDICT:classified_num_instances")
        .init(6)
        .flags(total | pdf | dist)
        .desc("Number of instances for different class of hyperblocks.");

    mispredictRateAllSeq
        .name(name() + ".MISPREDICT:allseq_misrate")
        .desc("Branch type mispredict rate for hyperblocks that produce seq target only.")
        .precision(6);

    mispredictRateAllSeq = branchMispredictsClassified[0] / numInstancesClassified[0];

    mispredictRateAllBranch
        .name(name() + ".MISPREDICT:allbranch_misrate")
        .desc("Branch type mispredict rate for hyperblocks that produce branch target only.")
        .precision(6);

    mispredictRateAllBranch = branchMispredictsClassified[1] / numInstancesClassified[1];

    mispredictRateAllCall
        .name(name() + ".MISPREDICT:allcall_misrate")
        .desc("Branch type mispredict rate for hyperblocks that produce call target only.")
        .precision(6);

    mispredictRateAllCall = branchMispredictsClassified[2] / numInstancesClassified[2];

    mispredictRateAllReturn
        .name(name() + ".MISPREDICT:allreturn_misrate")
        .desc("Branch type mispredict rate for hyperblocks that produce return target only.")
        .precision(6);

    mispredictRateAllReturn = branchMispredictsClassified[3] / numInstancesClassified[3];

    mispredictRateBothSeqAndOthers
        .name(name() + ".MISPREDICT:both_seqandothers_misrate")
        .desc("Branch type mispredict rate for hyperblocks that produce both seq and other targets.")
        .precision(6);

    mispredictRateBothSeqAndOthers = branchMispredictsClassified[4] / numInstancesClassified[4];

    mispredictRateOthers
        .name(name() + ".MISPREDICT:others_misrate")
        .desc("Branch type mispredict rate for hyperblocks that are other cases.")
        .precision(6);

    mispredictRateOthers = branchMispredictsClassified[5] / numInstancesClassified[5];

    branchTypeCompleteClasses
        .name(name() + ".branchTypeCompleteClasses")
        .desc("Distribution of number of types for each static hyperblock, indidrect types are included.")
        .flags(Stats::pdf)
        .init(0, 5, 1);

    branchTypePercentageInBlock
        .name(name() + ".branchTypePercentageInBlock")
        .desc("Average percentage of branch types in a block. 0:seq, 1:branch, 2:call, 3:ret")
        .init(TheISA::NumBranchType)
        .precision(6);

    branchTypePercentageOverall
        .name(name() + ".branchTypePercentageOverall")
        .desc("Average percentage of branch types across all dynamic block instances. 0:seq, 1:branch, 2:call, 3:ret")
        .init(TheISA::NumBranchType)
        .precision(6);

    staticBlockByNumTypes
        .name(name() + ".staticBlockByNumTypes")
        .desc("Categorise static blocks by number of exit types. 0: number of static"
              "blocks with only one type of exits, 1: number of static blocks with two"
              "types of exits, 2: number of static blocks with three types of exits."
              "Note that this stats will not include sequential target, since it's a"
              "dynamic concept.")
        .flags(Stats::pdf)
        .init(TheISA::NumBranchType - 1);

    staticBlockByNumExits
        .name(name() + ".staticBlockByNumExits")
        .desc("Categorise static blocks by number of exits. Maximum 8 is allowed.")
        .flags(Stats::pdf)
        .init(TheISA::MaxExitsInBlock);

    branchTypeMaxDist
        .name(name() + ".branchTypeMaxDist")
        .desc("Distribution of static instruction blocks with different most taken branch types")
        .init(0, TheISA::NumBranchType, 1)
        .flags(Stats::pdf);

    branchTypeTendencyType
        .name(name() + ".branchTypeTendencyType")
        .desc("Average percentage of most taken type of instruction blocks with different types of most taken types")
        .init(TheISA::NumBranchType)
        .precision(6);

    numCommittedDist
        .init(0,TheISA::MaxInstsInBlock,10)
        .name(name() + ".COM:committed_per_cycle")
        .desc("Number of useful insts commited each cycle")
        .flags(Stats::pdf)
        ;

    statComInstBlock
        .init(cpu->numThreads)
        .name(name() + ".COM:count")
        .desc("Number of inst blocks committed")
        .flags(total)
        ;

    commitEligible
        .init(cpu->numThreads)
        .name(name() + ".COM:bw_limited")
        .desc("number of insts not committed due to BW limits")
        .flags(total)
        ;

    commitEligibleSamples
        .name(name() + ".COM:bw_lim_events")
        .desc("number cycles where commit BW limit reached")
        ;

    commitExitID
        .init(0, TheISA::MaxExitsInBlock, 1)
        .name(name() + ".COM:commitExitID")
        .desc("Exit ID of each committed block")
        .flags(Stats::pdf);

    commitExitIDOfDiffNumExitBlocks
        .init(TheISA::MaxExitsInBlock, 0, TheISA::MaxExitsInBlock, 1)
        .name(name() + ".COM:commitExitIDOfDiffNumExitBlocks")
        .desc("Exit ID of committed block with different number of exits")
        .flags(Stats::pdf);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setThreads(std::vector<Thread *> &threads)
{
    thread = threads;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
{
    timeBuffer = tb_ptr;

    // Setup wire to send information back to Execute.
    toExecute = timeBuffer->getWire(0);

    // Setup wire to read data from IEW (for the ROB).
    robInfoFromExecute = timeBuffer->getWire(-executeToCommitDelay);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setFetchQueue(TimeBuffer<Fetch2Map> *fq_ptr)
{
    fetch2commitQueue = fq_ptr;

    fromFetch = fetch2commitQueue->getWire(-fetchToCommitDelay);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setMapQueue(TimeBuffer<Map2Execute> *rq_ptr)
{
    map2commitQueue = rq_ptr;

    // Setup wire to get instructions from rename (for the ROB).
    fromMap = map2commitQueue->getWire(-mapToROBDelay);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setExecuteQueue(TimeBuffer<Execute2Commit> *iq_ptr)
{
    execute2commitQueue = iq_ptr;

    // Setup wire to get instructions from IEW.
    fromExecute = execute2commitQueue->getWire(-executeToCommitDelay);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setExecuteStage(Execute *execute_stage)
{
    executeStage = execute_stage;
}

template<class Impl>
void
SimpleEdgeCommit<Impl>::setActiveThreads(list<ThreadID> *at_ptr)
{
    activeThreads = at_ptr;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setROB(ROB *rob_ptr)
{
    rob = rob_ptr;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::initStage()
{
    rob->setActiveThreads(activeThreads);
    rob->resetEntries();

    // Broadcast the number of free entries.
    for (ThreadID tid = 0; tid < numThreads; tid++) {
        toExecute->commitInfo[tid].usedROB = true;
        toExecute->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);
        toExecute->commitInfo[tid].emptyROB = true;
    }

    // Commit must broadcast the number of free entries it has at the
    // start of the simulation, so it starts as active.
    cpu->activateStage(CPU::CommitIdx);

    cpu->activityThisCycle();
    trapLatency = cpu->ticks(trapLatency);

    registerExitCallback(new CommitExitCallBack<Impl>(this));
}

template <class Impl>
bool
SimpleEdgeCommit<Impl>::drain()
{
    drainPending = true;

    return false;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::switchOut()
{
    switchedOut = true;
    drainPending = false;
    rob->switchOut();
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::resume()
{
    drainPending = false;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::takeOverFrom()
{
    switchedOut = false;
    _status = Active;
    _nextStatus = Inactive;
    for (ThreadID tid = 0; tid < numThreads; tid++) {
        commitStatus[tid] = Idle;
        changedROBNumEntries[tid] = false;
        trapSquash[tid] = false;
        tcSquash[tid] = false;
    }
    squashCounter = 0;
    rob->takeOverFrom();
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::updateStatus()
{
    // reset ROB changed variable
    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        changedROBNumEntries[tid] = false;

        // Also check if any of the threads has a trap pending
        if (commitStatus[tid] == TrapPending ||
            commitStatus[tid] == FetchTrapPending) {
            _nextStatus = Active;
        }
    }

    if (_nextStatus == Inactive && _status == Active) {
        DPRINTF(Activity, "Deactivating stage.\n");
        cpu->deactivateStage(CPU::CommitIdx);
    } else if (_nextStatus == Active && _status == Inactive) {
        DPRINTF(Activity, "Activating stage.\n");
        cpu->activateStage(CPU::CommitIdx);
    }

    _status = _nextStatus;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::setNextStatus()
{
    int squashes = 0;

    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (commitStatus[tid] == ROBSquashing) {
            squashes++;
        }
    }

    squashCounter = squashes;

    // If commit is currently squashing, then it will have activity for the
    // next cycle. Set its next status as active.
    if (squashCounter) {
        _nextStatus = Active;
    }
}

template <class Impl>
bool
SimpleEdgeCommit<Impl>::changedROBEntries()
{
    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (changedROBNumEntries[tid]) {
            return true;
        }
    }

    return false;
}

template <class Impl>
size_t
SimpleEdgeCommit<Impl>::numROBFreeEntries(ThreadID tid)
{
    return rob->numFreeEntries(tid);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::generateTrapEvent(ThreadID tid)
{
    DPRINTF(EdgeCommit, "Generating trap event for [tid:%i]\n", tid);

    TrapEvent *trap = new TrapEvent(this, tid);

    cpu->schedule(trap, curTick + trapLatency);
    trapInFlight[tid] = true;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::generateTCEvent(ThreadID tid)
{
    assert(!trapInFlight[tid]);
    DPRINTF(EdgeCommit, "Generating TC squash event for [tid:%i]\n", tid);

    tcSquash[tid] = true;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::squashAll(ThreadID tid)
{
    // If we want to include the squashing instruction in the squash,
    // then use one older sequence number.
    // Hopefully this doesn't mess things up.  Basically I want to squash
    // all instructions of this thread.
    TheISA::BlockID squashed_inst_block = rob->isEmpty() ?
        0 : rob->readHeadInstBlock(tid)->getBlockID() - 1;

    // All younger instructions will be squashed. Set the sequence
    // number as the youngest instruction in the ROB (0 in this case.
    // Hopefully nothing breaks.)
    youngestSeqNum[tid] = 0;

    int num_squashed = rob->squash(squashed_inst_block, tid);

    // Tell the previous stage how many inst blocks have been squashed
    // this cycle.
    toExecute->commitInfo[tid].numSquashedInstBlocks = num_squashed;

    changedROBNumEntries[tid] = true;

    // Send back the sequence number of the squashed instruction.
    toExecute->commitInfo[tid].doneBlockID = squashed_inst_block;

    // Send back the squash signal to tell stages that they should
    // squash.
    toExecute->commitInfo[tid].squash = true;

    // Send back the rob squashing signal so other stages know that
    // the ROB is in the process of squashing.
    toExecute->commitInfo[tid].robSquashing = true;

    toExecute->commitInfo[tid].branchMispredict = false;

    toExecute->commitInfo[tid].nextPC = PC[tid];
    toExecute->commitInfo[tid].nextNPC = PC[tid] + sizeof(TheISA::MachInst);
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::squashFromTrap(ThreadID tid)
{
    squashAll(tid);

    DPRINTF(EdgeCommit, "Squashing from trap, restarting at PC %#x\n", PC[tid]);

    thread[tid]->trapPending = false;
    thread[tid]->inSyscall = false;
    trapInFlight[tid] = false;

    trapSquash[tid] = false;

    commitStatus[tid] = ROBSquashing;
    cpu->activityThisCycle();
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::squashFromTC(ThreadID tid)
{
    squashAll(tid);

    DPRINTF(EdgeCommit, "Squashing from TC, restarting at PC %#x\n", PC[tid]);

    thread[tid]->inSyscall = false;
    assert(!thread[tid]->trapPending);

    commitStatus[tid] = ROBSquashing;
    cpu->activityThisCycle();

    tcSquash[tid] = false;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::tick()
{
    wroteToTimeBuffer = false;
    _nextStatus = Inactive;

    if (drainPending && rob->isEmpty() && !executeStage->hasStoresToWB()) {
        cpu->signalDrained();
        drainPending = false;
        return;
    }

    if (activeThreads->empty())
        return;

    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    // Check if any of the threads are done squashing.  Change the
    // status if they are done.
    while (threads != end) {
        ThreadID tid = *threads++;

        // Clear the bit saying if the thread has committed stores
        // this cycle.
        committedStores[tid] = false;

        if (commitStatus[tid] == ROBSquashing) {

            if (rob->isDoneSquashing(tid)) {
                commitStatus[tid] = Running;
            } else {
                DPRINTF(EdgeCommit,"[tid:%u]: Still Squashing, cannot commit any"
                        " inst blocks this cycle.\n", tid);
                int num_squashed = rob->doSquash(tid);
                // Tell the previous stage how many inst blocks have
                // been squashed this cycle.
                toExecute->commitInfo[tid].numSquashedInstBlocks = num_squashed;
                toExecute->commitInfo[tid].robSquashing = true;
                wroteToTimeBuffer = true;
            }
        }
    }

    commit();

    markCompletedInstBlocks();

    threads = activeThreads->begin();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (!rob->isEmpty(tid) && (rob->readHeadInstBlock(tid))->readyToCommit()) {
            // The ROB has more instruction blocks it can commit. Its next status
            // will be active.
            _nextStatus = Active;

            EdgeBlockPtr inst_block = rob->readHeadInstBlock(tid);

            DPRINTF(EdgeCommit,"[tid:%i]: Inst block [id:%lli] PC %#x is head of"
                    " ROB and ready to commit\n",
                    tid, inst_block->getBlockID(), inst_block->getStartPC());

        } else if (!rob->isEmpty(tid)) {
            EdgeBlockPtr inst_block = rob->readHeadInstBlock(tid);

            DPRINTF(EdgeCommit,"[tid:%i]: Can't commit, Inst block [id:%lli] PC "
                    "%#x is head of ROB and not ready\n",
                    tid, inst_block->getBlockID(), inst_block->getStartPC());
        }

        DPRINTF(EdgeCommit, "[tid:%i]: ROB has %d inst blocks & %d free entries.\n",
                tid, rob->countInstBlocks(tid), rob->numFreeEntries(tid));
    }


    if (wroteToTimeBuffer) {
        DPRINTF(Activity, "Activity This Cycle.\n");
        cpu->activityThisCycle();
    }

    updateStatus();
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::commit()
{
    ////////////////////////////////////
    // Check for any possible squashes, handle them first
    ////////////////////////////////////
    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        // Not sure which one takes priority.  I think if we have
        // both, that's a bad sign.
        if (trapSquash[tid] == true) {
            assert(!tcSquash[tid]);
            squashFromTrap(tid);
        } else if (tcSquash[tid] == true) {
            assert(commitStatus[tid] != TrapPending);
            squashFromTC(tid);
        }

        // Squashed sequence number must be older than youngest valid
        // instruction in the ROB. This prevents squashes from younger
        // instructions overriding squashes from older instructions.
        if (fromExecute->squash[tid] &&
            commitStatus[tid] != TrapPending &&
            fromExecute->squashedSeqNum[tid] <= youngestSeqNum[tid]) {

            DPRINTF(EdgeCommit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n",
                    tid,
                    fromExecute->mispredPC[tid],
                    fromExecute->squashedSeqNum[tid]);

            DPRINTF(EdgeCommit, "[tid:%i]: Redirecting to PC %#x\n",
                    tid,
                    fromExecute->nextPC[tid]);

            commitStatus[tid] = ROBSquashing;

            // If we want to include the squashing instruction in the squash,
            // then use one older sequence number.
            InstSeqNum squashed_inst_block = fromExecute->squashedSeqNum[tid];

            if (fromExecute->includeSquashInstBlock[tid] == true) {
                squashed_inst_block--;
            }

            // All younger instructions will be squashed. Set the sequence
            // number as the youngest instruction in the ROB.
            youngestSeqNum[tid] = squashed_inst_block;

            int num_squashed = rob->squash(squashed_inst_block, tid);

            // Tell the previous stage how many inst blocks have been
            // squashed this cycle.
            toExecute->commitInfo[tid].numSquashedInstBlocks = num_squashed;

            changedROBNumEntries[tid] = true;

            toExecute->commitInfo[tid].doneBlockID = squashed_inst_block;

            toExecute->commitInfo[tid].squash = true;

            // Send back the rob squashing signal so other stages know that
            // the ROB is in the process of squashing.
            toExecute->commitInfo[tid].robSquashing = true;

            toExecute->commitInfo[tid].branchMispredict =
                fromExecute->branchMispredict[tid];
            toExecute->commitInfo[tid].exitMispredict = 
                fromExecute->exitMispredict[tid];
            toExecute->commitInfo[tid].seqMispredict =
                fromExecute->seqMispredict[tid];

            if (fromExecute->branchMispredict[tid]) {
                toExecute->commitInfo[tid].exitID = fromExecute->exitID;
                toExecute->commitInfo[tid].exitType = fromExecute->exitType;
            }

            toExecute->commitInfo[tid].branchTaken =
                fromExecute->branchTaken[tid];

            toExecute->commitInfo[tid].exitID =
                fromExecute->exitID;
            toExecute->commitInfo[tid].predExitID =
                fromExecute->predExitID;
            toExecute->commitInfo[tid].exitType =
                fromExecute->exitType;
            toExecute->commitInfo[tid].predExitType =
                fromExecute->predExitType;

            toExecute->commitInfo[tid].nextPC = fromExecute->nextPC[tid];
            toExecute->commitInfo[tid].nextNPC = fromExecute->nextPC[tid]
                                                 + sizeof(TheISA::MachInst);

            toExecute->commitInfo[tid].mispredPC = fromExecute->mispredPC[tid];
        }

    }

    setNextStatus();

    if (squashCounter != numThreads) {
        // If we're not currently squashing, then get instructions.
        getInstBlocks();

        // Try to commit any instructions.
        commitInstBlocks();
    }

    //Check for any activity
    threads = activeThreads->begin();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (changedROBNumEntries[tid]) {
            toExecute->commitInfo[tid].usedROB = true;
            toExecute->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);

            wroteToTimeBuffer = true;
            changedROBNumEntries[tid] = false;
            if (rob->isEmpty(tid))
                checkEmptyROB[tid] = true;
        }

        // ROB is only considered "empty" for previous stages if: a)
        // ROB is empty, b) there are no outstanding stores, c) IEW
        // stage has received any information regarding stores that
        // committed.
        // c) is checked by making sure to not consider the ROB empty
        // on the same cycle as when stores have been committed.
        if (checkEmptyROB[tid] && rob->isEmpty(tid) &&
            !executeStage->hasStoresToWB(tid) && !committedStores[tid]) {
            checkEmptyROB[tid] = false;
            toExecute->commitInfo[tid].usedROB = true;
            toExecute->commitInfo[tid].emptyROB = true;
            toExecute->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid);
            wroteToTimeBuffer = true;
        }

    }
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::commitInstBlocks()
{
    ////////////////////////////////////
    // Handle commit
    // Note that commit will be handled prior to putting new
    // instructions in the ROB so that the ROB only tries to commit
    // instructions it has in this current cycle, and not instructions
    // it is writing in during this cycle.  Can't commit and squash
    // things at the same time...
    ////////////////////////////////////

    DPRINTF(EdgeCommit, "Trying to commit inst blocks in the ROB.\n");

    unsigned num_committed = 0;
    int committed_insts = 0;

    EdgeBlockPtr head_inst_block;

    // Commit as many inst blocks as possible until the commit bandwidth
    // limit is reached, or it becomes impossible to commit any more.
    while (num_committed < commitWidth) {

        int commit_thread = getCommittingThread();

        if (commit_thread == -1 || !rob->isHeadReady(commit_thread))
            break;

        head_inst_block = rob->readHeadInstBlock(commit_thread);

        ThreadID tid = head_inst_block->getTid();

        assert(tid == commit_thread);

        DPRINTF(EdgeCommit, "Trying to commit head inst block, [id:%i] [tid:%i]\n",
                head_inst_block->getBlockID(), tid);

        // If the head instruction is squashed, it is ready to retire
        // (be removed from the ROB) at any time.
        if (head_inst_block->isSquashed()) {

            DPRINTF(EdgeCommit, "Retiring squashed inst block from "
                    "ROB.\n");

            rob->retireHead(commit_thread);

            ++commitSquashedInsts;

            // Record that the number of ROB entries has changed.
            changedROBNumEntries[tid] = true;
        } else {
            PC[tid] = head_inst_block->getStartPC();
            nextPC[tid] = head_inst_block->getBranchTarget();

            // Increment the total number of non-speculative instructions
            // executed.
            // Hack for now: it really shouldn't happen until after the
            // commit is deemed to be successful, but this count is needed
            // for syscalls.
            thread[tid]->funcExeInst++;

            // Try to commit the head inst block.
            bool commit_success = commitHead(head_inst_block, num_committed);

            if (commit_success) {

                ++num_committed;

                changedROBNumEntries[tid] = true;
#if TRACING_ON
                std::string sym_str;
                Addr sym_addr;
                debugSymbolTable->findNearestSymbol(head_inst_block->getStartPC(),
                                                    sym_str, sym_addr);
                DPRINTFR(EdgeCommitResultSym, ".BLOCK %s\n",
                         sym_str.c_str());
#endif

#ifndef NDEBUG
                head_inst_block->dumpOutput();
#endif
                DPRINTF(EdgeCommitResult, "Successfully commited inst block[Bid%lli],"
                        " inform fetch to redirect to %#x\n",
                        head_inst_block->getBlockID(),
                        head_inst_block->getBranchTarget());

                toExecute->commitInfo[tid].doneBlockID = head_inst_block->getBlockID();
                toExecute->commitInfo[tid].frameID = head_inst_block->getFrameID();
                toExecute->commitInfo[tid].blockPC = head_inst_block->getStartPC();

                toExecute->commitInfo[tid].exitID = head_inst_block->getExitID();
                toExecute->commitInfo[tid].predExitID =
                    head_inst_block->getPredExitID();

                toExecute->commitInfo[tid].exitMispredict =
                    head_inst_block->exitMispredicted();
                toExecute->commitInfo[tid].exitType = head_inst_block->getExitType();
                toExecute->commitInfo[tid].predExitType = 
                    head_inst_block->getPredExitType();
                toExecute->commitInfo[tid].seqMispredict =
                    head_inst_block->seqMispredicted();

                toExecute->commitInfo[tid].nextPC = head_inst_block->getBranchTarget();
                toExecute->commitInfo[tid].nextNPC =
                    head_inst_block->getBranchTarget() + sizeof(TheISA::MachInst);

                if (head_inst_block->isNeedSyscall()) {
                    toExecute->commitInfo[tid].needSyscall = true;
                }

                ++commitCommittedInstBlocks;

                if (head_inst_block->getExitType() == TheISA::seq) {
                    ++commitCommittedSeqInstBlocks;
                }

                commitCommittedInsts += head_inst_block->getNumUsefulInst();
                commitCommittedWrites += head_inst_block->getNumRegWriteInst();
                commitCommittedReads += head_inst_block->getNumRegReadInst();

                commitCommittedNopInsts += head_inst_block->getBlockSize() 
                    - head_inst_block->getNumUsefulInst();

                // Moves are excluded.
                int executed_useful_insts = 
                    head_inst_block->executedInsts - 
                    head_inst_block->executedMovs;

                assert(executed_useful_insts >= 0);

                cpu->instBlockDone(tid, executed_useful_insts);

                committed_insts += executed_useful_insts;

                if (simPointSim) {
                    if (simPoint->checkEndPoint(head_inst_block)) {
                        Event *limit_event =
                            new SimLoopExitEvent("simulate() limit reached", 0);
                        // Assume to exit this cycle.
                        cpu->schedule(limit_event, curTick + 1);
                    }
                }

                PC[tid] = nextPC[tid];

                int count = 0;
                Addr oldpc;
                // Debug statement.  Checks to make sure we're not
                // currently updating state while handling PC events.
                assert(!thread[tid]->inSyscall && !thread[tid]->trapPending);
                do {
                    oldpc = PC[tid];
                    cpu->system->pcEventQueue.service(thread[tid]->getTC());
                    count++;
                } while (oldpc != PC[tid]);

                if (count > 1) {
                    DPRINTF(EdgeCommit,
                        "PC skip function event, stopping commit\n");
                    break;
                }

            } else {
                DPRINTF(EdgeCommit, "Unable to commit head instruction PC:%#x "
                        "[tid:%i] [sn:%i].\n",
                        head_inst_block->getStartPC(),
                        tid,
                        head_inst_block->getBlockID());
                break;
            }
        }
    }

    DPRINTF(EdgeCommit, "%i\n", num_committed);
    numCommittedDist.sample(committed_insts);

    if (num_committed == commitWidth) {
        commitEligibleSamples++;
    }
}

template <class Impl>
bool
SimpleEdgeCommit<Impl>::commitHead(EdgeBlockPtr &head_inst_block, unsigned inst_block_num)
{
    assert(head_inst_block);

    ThreadID tid = head_inst_block->getTid();

    // Check if the inst block caused a fault.  If so, trap.
    Fault inst_block_fault = head_inst_block->getFault();

    if (inst_block_fault != NoFault) {
        DPRINTF(EdgeCommit, "Inst block[sn:%lli] PC %#x has a fault %s.\n",
                head_inst_block->getBlockID(), head_inst_block->getStartPC(),
                inst_block_fault->name());

        if (executeStage->hasStoresToWB(tid) || inst_block_num > 0) {
            DPRINTF(EdgeCommit, "Stores outstanding, fault must wait.\n");
            return false;
        }

        head_inst_block->setCompleted();

        assert(!thread[tid]->inSyscall);

        // Mark that we're in state update mode so that the trap's
        // execution doesn't generate extra squashes.
        thread[tid]->inSyscall = true;

        // Execute the trap.  Although it's slightly unrealistic in
        // terms of timing (as it doesn't wait for the full timing of
        // the trap event to complete before updating state), it's
        // needed to update the state as soon as possible.  This
        // prevents external agents from changing any specific state
        // that the trap need.
        cpu->trap(inst_block_fault, tid);

        // Exit state update mode to avoid accidental updating.
        thread[tid]->inSyscall = false;

        commitStatus[tid] = TrapPending;

        // Generate trap squash event.
        generateTrapEvent(tid);

        return false;
    }

    updateComInstBlockStats(head_inst_block);

    // Finally clear the head ROB entry.
    rob->retireHead(tid);

    // If this was a store, record it for this cycle.
    if (head_inst_block->getNumStoreInst() != 0)
        committedStores[tid] = true;

    // Return true to indicate that we have committed an instruction.
    return true;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::getInstBlocks()
{
    DPRINTF(EdgeCommit, "Getting inst blocks from Map stage.\n");

    // Read any mapped inst blocks and place them into the ROB.
    int inst_blocks_to_process = std::min((int)mapWidth, fromMap->size);

    for (int inst_block_num = 0; inst_block_num < inst_blocks_to_process; ++inst_block_num) {
        EdgeBlockPtr inst_block;

        inst_block = fromMap->instBlocks[inst_block_num];
        ThreadID tid = inst_block->getTid();

        if (!inst_block->isSquashed() &&
            commitStatus[tid] != ROBSquashing &&
            commitStatus[tid] != TrapPending) {
            changedROBNumEntries[tid] = true;

            DPRINTF(EdgeCommit, "Inserting PC %#x [id:%i] [tid:%i] into ROB.\n",
                    inst_block->getStartPC(), inst_block->getBlockID(), tid);

            rob->insertInstBlock(inst_block);

            DPRINTF(EdgeCommit, "%i entries have been used in ROB. Max is %i\n",
                    rob->getThreadEntries(tid),
                    rob->getMaxEntries(tid));

            assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid));

            youngestSeqNum[tid] = inst_block->getBlockID();
        } else {
            DPRINTF(EdgeCommit, "Inst block PC %#x [sn:%i] [tid:%i] was "
                    "squashed, skipping.\n",
                    inst_block->getStartPC(),
                    inst_block->getBlockID(), tid);
        }
    }
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::skidInsert()
{
    DPRINTF(EdgeCommit, "Attempting to any inst blocks from rename into "
            "skidBuffer.\n");

    for (int inst_block_num = 0; inst_block_num < fromMap->size; ++inst_block_num) {

        EdgeBlockPtr inst_block = fromMap->instBlocks[inst_block_num];

        if (!inst_block->isSquashed()) {
            DPRINTF(EdgeCommit, "Inserting PC %#x [sn:%i] [tid:%i] into ",
                    "skidBuffer.\n",
                    inst_block->getStartPC(),
                    inst_block->getBlockID(),
                    inst_block->getTid());

            skidBuffer.push(inst_block);
        } else {
            DPRINTF(EdgeCommit, "Inst block PC %#x [sn:%i] [tid:%i] was "
                    "squashed, skipping.\n",
                    inst_block->getStartPC(),
                    inst_block->getBlockID(),
                    inst_block->getTid());
        }
    }
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::markCompletedInstBlocks()
{
    // Grab completed insts out of the Execute inst block queue, and mark
    // inst blocks completed within the ROB.
    for (int inst_block_num = 0;
         inst_block_num < fromExecute->size && fromExecute->instBlocks[inst_block_num];
         ++inst_block_num)
    {
        if (!(fromExecute->instBlocks[inst_block_num])->isSquashed()) {
            DPRINTF(EdgeCommit, "[tid:%i]: Marking PC %#x, [sn:%lli] ready "
                    "within ROB.\n",
                    fromExecute->instBlocks[inst_block_num]->getTid(),
                    fromExecute->instBlocks[inst_block_num]->getStartPC(),
                    fromExecute->instBlocks[inst_block_num]->getBlockID());

            // Mark the inst block as ready to commit.
            fromExecute->instBlocks[inst_block_num]->setCanCommit();
        }
    }
}

template <class Impl>
bool
SimpleEdgeCommit<Impl>::robDoneSquashing()
{
    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (!rob->isDoneSquashing(tid))
            return false;
    }

    return true;
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::updateComInstBlockStats(EdgeBlockPtr &inst_block)
{
    ThreadID tid = inst_block->getTid();
    statComInstBlock[tid]++;

    dynamicBlockByBranchType[inst_block->getExitType()]++;

    // Record the exit id 
    TheISA::ExitID exit_id = inst_block->getExitID();
    int exit_num = inst_block->getExitNum();

    assert(exit_id < TheISA::MaxExitsInBlock);
    assert(exit_num <= TheISA::MaxExitsInBlock && exit_num > 0);

    commitExitID.sample(exit_id);

    commitExitIDOfDiffNumExitBlocks[exit_num - 1].sample(exit_id);

    // Some stuffs related to mispredicts
    bool branch_mispredict = inst_block->misPredicted();
    bool exit_mispredict = inst_block->exitMispredicted();
    bool branch_type_mispredict = inst_block->exitTypeMispredicted();

    if (branch_mispredict) {
        ++branchMispredicts;
        toExecute->commitInfo[tid].branchMispredict = true;

        // Record the situation when exit and type are both
        // mispredicted
        if (exit_mispredict && branch_type_mispredict) {
            exitAndTypeMispredicts++;
        }

        TheISA::BTypeID real_type = inst_block->getExitType();
        assert(real_type < TheISA::InvalidExitType);
        branchTargetBuffersMispredicts[real_type]++;

        // Record the situation when exit and branch target are both
        // mispredicted
        if (exit_mispredict) {
            ++exitAndBranchMispredicts;
        } else if (branch_type_mispredict) {
            assert(inst_block->getExitType() < TheISA::InvalidExitType);

            branchTypeMispredicts[0]++;

            if (inst_block->getExitType() == TheISA::seq) {
                seqBranchTypeMispredicts[0]++;
            } else if (inst_block->getPredExitType() == TheISA::seq) {
                seqBranchTypeMispredicts[1]++;
            }
        } else {
            assert(inst_block->getExitType() < TheISA::InvalidExitType);
            assert(inst_block->getExitType() > TheISA::seq);
            // If only branch target is mispredicted, record the
            // branch type
            branchTypeMispredicts[inst_block->getExitType()]++;
        }
    } else {
        ++correctPredictions;
    }

    if (exit_mispredict) {
        ++exitMispredicts;
    }

    if (branch_type_mispredict) {
        ++branchTypeOnlyMispredicts;

        branchTypeOnlyMispredictsBreakDown[inst_block->getExitType()]++;
    }

    if (exit_mispredict && branch_type_mispredict) {
        ++exitAndTypeOnlyMispredicts;
    }

    Addr block_addr = inst_block->getStartPC();

    typename m5::hash_map<Addr, StaticInstBlockEntry*>::iterator it =
        staticInstBlockMap.find(block_addr);
    // Record the information of dynamic instances to a specific
    // static instruction block
    if (it == staticInstBlockMap.end()) {
        StaticInstBlockEntry * new_entry = new StaticInstBlockEntry();

        // 0: times this instance produces a seq target
        // 1: times this instance produces a btb target
        // 2: times this instance produces a ctb target
        // 3: times this instance produces a rte target
        // 4: number of exit types of this hyperblock
        // 5: mispredicts of this hyperblock
        // 6: number of exits of this hyperblock
//        new_entry->resize(TheISA::NumBranchType + 3, static_cast<int>(0));

        assert(inst_block->getExitType() >=0 &&
                inst_block->getExitType() < TheISA::NumBranchType);

        ((*new_entry).numInstancesByTakenTypes)[inst_block->getExitType()]++;

        int num_type = inst_block->getNumTypes();
        int num_exit = inst_block->getExitNum();
        int complete_num_type = inst_block->getNumCompleteTypes();

        assert(num_type > 0 && num_type <= TheISA::NumBranchType);
        assert(num_exit > 0 && num_exit <= TheISA::MaxExitsInBlock);
        assert(complete_num_type > 0 && complete_num_type <= 5);

        branchTypeCompleteClasses.sample(complete_num_type);

        DPRINTF(EdgeBlockType, "Block[PC:%#x], num_type: %i,"
                " complete_num_type: %i, num_exit: %i\n",
                block_addr,
                num_type, complete_num_type,
                num_exit);

//        (*new_entry)[TheISA::NumBranchType] = num_type;
        (*new_entry).numTypes = num_type;
//        (*new_entry)[TheISA::NumBranchType + 2] = num_exit;
        (*new_entry).numExits = num_exit;

        if (branch_type_mispredict) {
//            ++((*new_entry)[TheISA::NumBranchType + 1]);
            ++((*new_entry).numMispredicts);
        }

        staticInstBlockMap.insert(std::make_pair(block_addr, new_entry));
    } else {
//        (*(it->second))[inst_block->getExitType()]++;

        assert(inst_block->getExitType() >=0 &&
                inst_block->getExitType() < TheISA::NumBranchType);

        ((*(it->second)).numInstancesByTakenTypes)[inst_block->getExitType()]++;

        assert(inst_block->getNumTypes() ==
                (*(it->second)).numTypes);
        assert(inst_block->getExitNum() ==
                (*(it->second)).numExits);

        if (branch_type_mispredict) {
//            ++((*(it->second))[TheISA::NumBranchType + 1]);
            ++((*(it->second)).numMispredicts);
        }
    }
}

////////////////////////////////////////
//                                    //
//  SMT COMMIT POLICY MAINTAINED HERE //
//                                    //
////////////////////////////////////////
template <class Impl>
ThreadID
SimpleEdgeCommit<Impl>::getCommittingThread()
{
    if (numThreads > 1) {
        switch (commitPolicy) {

          case Aggressive:
            //If Policy is Aggressive, commit will call
            //this function multiple times per
            //cycle
            return oldestReady();

          case RoundRobin:
            return roundRobin();

          case OldestReady:
            return oldestReady();

          default:
            return InvalidThreadID;
        }
    } else {
        assert(!activeThreads->empty());
        ThreadID tid = activeThreads->front();

        if (commitStatus[tid] == Running ||
            commitStatus[tid] == Idle ||
            commitStatus[tid] == FetchTrapPending) {
            return tid;
        } else {
            return InvalidThreadID;
        }
    }
}

template<class Impl>
ThreadID
SimpleEdgeCommit<Impl>::roundRobin()
{
    list<ThreadID>::iterator pri_iter = priority_list.begin();
    list<ThreadID>::iterator end      = priority_list.end();

    while (pri_iter != end) {
        ThreadID tid = *pri_iter;

        if (commitStatus[tid] == Running ||
            commitStatus[tid] == Idle ||
            commitStatus[tid] == FetchTrapPending) {

            if (rob->isHeadReady(tid)) {
                priority_list.erase(pri_iter);
                priority_list.push_back(tid);

                return tid;
            }
        }

        pri_iter++;
    }

    return InvalidThreadID;
}

template<class Impl>
ThreadID
SimpleEdgeCommit<Impl>::oldestReady()
{
    unsigned oldest = 0;
    bool first = true;

    list<ThreadID>::iterator threads = activeThreads->begin();
    list<ThreadID>::iterator end = activeThreads->end();

    while (threads != end) {
        ThreadID tid = *threads++;

        if (!rob->isEmpty(tid) &&
            (commitStatus[tid] == Running ||
             commitStatus[tid] == Idle ||
             commitStatus[tid] == FetchTrapPending)) {

            if (rob->isHeadReady(tid)) {

                EdgeBlockPtr head_inst_block = rob->readHeadInstBlock(tid);

                if (first) {
                    oldest = tid;
                    first = false;
                } else if (head_inst_block->getBlockID() < oldest) {
                    oldest = tid;
                }
            }
        }
    }

    if (!first) {
        return oldest;
    } else {
        return InvalidThreadID;
    }
}

template <class Impl>
void
SimpleEdgeCommit<Impl>::dumpInstBlockStat()
{
    // Firstly, I'd like to copy content of the hash_map to a vector,
    // because i need to sort the content.

    std::vector<pair<Addr, StaticInstBlockEntry*> > static_inst_block_vec; 

    static_inst_block_vec.resize(staticInstBlockMap.size());

    std::copy(staticInstBlockMap.begin(), staticInstBlockMap.end(),
            static_inst_block_vec.begin());

    double tendency_sum = 0.0;
    int64_t num_inst_block = 0;
    int64_t num_max[TheISA::NumBranchType] = {0};
    double seq_sum_max[TheISA::NumBranchType] = {0.0};

    int64_t num_seq = 0;
    int64_t num_branch = 0;
    int64_t num_call = 0;
    int64_t num_ret = 0;
    int64_t num_instances = 0;

    double seq_percent_sum = 0.0;
    double branch_percent_sum = 0.0;
    double call_percent_sum = 0.0;
    double ret_percent_sum = 0.0;

    // Before we do our calculation, we need to sort the content in an
    // descend order of the called instances.
    std::sort(static_inst_block_vec.begin(), static_inst_block_vec.end(),
            staticEntryOp());

    typename std::vector<pair<Addr, StaticInstBlockEntry*> >::iterator it =
        static_inst_block_vec.begin();
    typename std::vector<pair<Addr, StaticInstBlockEntry*> >::iterator end =
        static_inst_block_vec.end();

    int top_i = 0;

    while (it != end) {
        assert(it->second);

        StaticInstBlockEntry entry = *(it->second);

//        cprintf("\nAddr: %#x\n", it->first);
        double temp_percent = 0.0;
        int64_t max = *(std::max_element(entry.numInstancesByTakenTypes.begin(),
                entry.numInstancesByTakenTypes.end()));
//        cprintf("\nMax: %i\n", max)

        int max_index = TheISA::InvalidExitType;

        for (int i = 0; i < TheISA::NumBranchType; i++) {
            if (max == (entry.numInstancesByTakenTypes)[i]) {
                max_index = i;
                break;
            }
        }

        assert(max_index < TheISA::InvalidExitType &&
                max_index >= TheISA::seq);

        int64_t sum = accumulate(entry.numInstancesByTakenTypes.begin(),
                entry.numInstancesByTakenTypes.end(), 0);

        temp_percent = (double)max / (double)sum;
        tendency_sum += temp_percent;
        seq_sum_max[max_index] += temp_percent;

        num_instances += sum;

        num_seq += (entry.numInstancesByTakenTypes)[TheISA::seq];
        num_branch += (entry.numInstancesByTakenTypes)[TheISA::branch];
        num_call += (entry.numInstancesByTakenTypes)[TheISA::call];
        num_ret += (entry.numInstancesByTakenTypes)[TheISA::ret];

        double seq_percent =
            (double)(entry.numInstancesByTakenTypes)[TheISA::seq] / (double)sum;
        double branch_percent =
            (double)(entry.numInstancesByTakenTypes)[TheISA::branch] / (double)sum;
        double call_percent =
            (double)(entry.numInstancesByTakenTypes)[TheISA::call] / (double)sum;
        double ret_percent =
            (double)(entry.numInstancesByTakenTypes)[TheISA::ret] / (double)sum;

        seq_percent_sum += seq_percent;
        branch_percent_sum += branch_percent;
        call_percent_sum += call_percent; 
        ret_percent_sum += ret_percent;

        if (top_i < 100) {
            branchTypeTop100SeqPercent[top_i] = seq_percent;
            branchTypeTop100BranchPercent[top_i] = branch_percent;
            branchTypeTop100CallPercent[top_i] = call_percent;
            branchTypeTop100RetPercent[top_i] = ret_percent;

            top_i++;
        }

        double coverage = (double) num_instances /
            (double)(commitCommittedInstBlocks.value());

        // Consider all hyperblocks
        if (coverage <= 1.0) {
//            int num_type = entry[TheISA::NumBranchType];
            int num_type = entry.numTypes;
            num_type--;

            assert(num_type >= 0);

            if (seq_percent == 1.0) {

                branchTypeTopNClasses[num_type][0]++;
//                branchMispredictsClassified[0] += entry[TheISA::NumBranchType + 1];
                branchMispredictsClassified[0] += entry.numMispredicts;
                numInstancesClassified[0] += sum;

            } else if (branch_percent == 1.0) {

                branchTypeTopNClasses[num_type][1]++;
                branchMispredictsClassified[1] += entry.numMispredicts;
                numInstancesClassified[1] += sum;

            } else if (call_percent == 1.0) {

                branchTypeTopNClasses[num_type][2]++;
                branchMispredictsClassified[2] += entry.numMispredicts;
                numInstancesClassified[2] += sum;

            } else if (ret_percent == 1.0) {

                branchTypeTopNClasses[num_type][3]++;
                branchMispredictsClassified[3] += entry.numMispredicts;
                numInstancesClassified[3] += sum;
                
            } else if (seq_percent > 0.0 && seq_percent < 1.0) {

                branchTypeTopNClasses[num_type][4]++;
                branchMispredictsClassified[4] += entry.numMispredicts;
                numInstancesClassified[4] += sum;

            } else {

                branchTypeTopNClasses[num_type][5]++;
                branchMispredictsClassified[5] += entry.numMispredicts;
                numInstancesClassified[5] += sum;

            }
        }

        int num_type = entry.numTypes;
        int num_exit = entry.numExits;

        assert(num_type > 0 && num_type <= TheISA::NumBranchType);
        assert(num_exit > 0 && num_type <= TheISA::MaxExitsInBlock);

        staticBlockByNumTypes[num_type - 1]++;
        staticBlockByNumExits[num_exit - 1]++;

//        cprintf("\nSum: %i\n", sum);

//        for (int i = 0; i < TheISA::NumBranchType; i++) {
//            cprintf("\ntype %i: %i\n", i, entry[i]);
//        }

        delete it->second;
        it++;
        num_inst_block++;

        branchTypeMaxDist.sample(max_index);

        num_max[max_index]++;
    }

    assert(num_inst_block > 0);

    branchTypeTendencyInBlock = tendency_sum / (double)num_inst_block;

    for (int i = 0; i < TheISA::InvalidExitType; i++) {
        branchTypeTendencyType[i] = seq_sum_max[i] / (double)num_max[i];
    }

    cprintf("seq:%i, branch:%i, call:%i, ret:%i, instances:%i,"
            " static number: %i\n",
            num_seq, num_branch, num_call, num_ret,
            num_instances, num_inst_block);

    branchTypePercentageOverall[TheISA::seq] = (double)(num_seq) /
        (double)(num_instances);
    branchTypePercentageOverall[TheISA::branch] = (double)(num_branch) /
        (double)(num_instances);
    branchTypePercentageOverall[TheISA::call] = (double)(num_call) /
        (double)(num_instances);
    branchTypePercentageOverall[TheISA::ret] = (double)(num_ret) /
        (double)(num_instances);

    branchTypePercentageInBlock[TheISA::seq] = seq_percent_sum /
        (double)num_inst_block;
    branchTypePercentageInBlock[TheISA::branch] = branch_percent_sum /
        (double)num_inst_block;
    branchTypePercentageInBlock[TheISA::call] = call_percent_sum /
        (double)num_inst_block;
    branchTypePercentageInBlock[TheISA::ret] = ret_percent_sum /
        (double)num_inst_block;
}

